package com.ssrs.framework.util;

import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.*;

/**
 * 自定义redis工具类，使用Redisson作为客户端，引入Redlock，进行分布式锁
 *
 * @author ssrs
 */
@Slf4j
@ConditionalOnProperty(value = "spring.cache.type", havingValue = "redis")
public class RedisUtil {
    private static RedissonClient redissonClient;

    private static RedisTemplate<String, String> redis;

    @Autowired
    private RedissonClient redisson;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @PostConstruct
    private void init() {
        redis = this.redisTemplate;
        redissonClient = this.redisson;
    }

    /**
     * set String
     *
     * @param key
     * @param value
     */
    public static void setString(String key, String value) {
        redis.opsForValue().set(key, value);
    }

    /**
     * set String
     *
     * @param key
     * @param value
     * @param leaseSeconds 过期时间，单位秒
     */
    public static void setString(String key, String value, long leaseSeconds) {
        redis.opsForValue().set(key, value, leaseSeconds, TimeUnit.SECONDS);
    }

    /**
     * 检查key是否存在
     *
     * @param key
     * @return
     */
    public static boolean checkKey(String key) {
        return redis.hasKey(key);
    }

    /**
     * 获取key的值
     *
     * @param key
     * @return
     */
    public static String get(String key) {
        return redis.opsForValue().get(key);
    }

    /**
     * set hash
     *
     * @param key
     * @param filed
     * @param value
     */
    public static void hset(String key, String filed, String value) {
        redis.opsForHash().put(key, filed, value);
    }

    /**
     * 获取key的hash values
     *
     * @param key
     * @return
     */
    public static long hlen(String key) {
        return redis.opsForHash().size(key);
    }

    public static Cursor<Map.Entry<Object, Object>> hscan(String key, ScanOptions options) {
        return redis.opsForHash().scan(key, options);
    }

    /**
     * set hash
     *
     * @param key
     * @param filed
     * @param value
     * @param leaseSeconds 过期时间，单位秒
     */
    public static void hset(String key, String filed, String value, long leaseSeconds) {
        redis.opsForHash().put(key, filed, value);
        expire(key, leaseSeconds);
    }

    /**
     * @param key
     * @param filed
     */
    public static void hdel(String key, String filed) {
        redis.opsForHash().delete(key, filed);
    }

    /**
     * get hash
     *
     * @param key
     * @param filed
     * @return
     */
    public static String hget(String key, String filed) {
        if (redis.opsForHash().get(key, filed) == null) {
            return null;
        }
        return redis.opsForHash().get(key, filed).toString();
    }

    public static Map<Object, Object> hgetAll(String key) {
        if (redis.opsForHash().entries(key) == null) {
            return null;
        }
        return redis.opsForHash().entries(key);
    }

    /**
     * 删除key
     *
     * @param key
     */
    public static void del(String key) {
        redis.delete(key);
    }

    /**
     * 自增
     *
     * @param key
     * @param delta
     * @return
     */
    public static long increment(String key, long delta) {
        return redis.opsForValue().increment(key, delta);
    }

    /**
     * 自增1
     *
     * @param key
     * @return
     */
    public static long increment(String key) {
        return redis.opsForValue().increment(key);
    }

    /**
     * 设置过期时间，单位秒
     *
     * @param key
     * @param leaseSeconds
     */
    public static void expire(String key, long leaseSeconds) {
        redis.expire(key, leaseSeconds, TimeUnit.SECONDS);
    }

    /**
     * 获取过期时间
     *
     * @param key
     * @return
     */
    public static long getExpire(String key) {
        return redis.getExpire(key);
    }

    /**
     * 自减
     *
     * @param key
     * @param delta
     * @return
     */
    public static long decrement(String key, long delta) {
        return redis.opsForValue().decrement(key, delta);
    }

    /**
     * 自减1
     *
     * @param key
     * @return
     */
    public static long decrement(String key) {
        return redis.opsForValue().decrement(key);
    }

    /**
     * 加锁
     *
     * @param key
     * @param leaseSeconds 过期时间，单位秒
     * @param waitSeconds  等待时间，单位秒
     * @return
     */
    public static boolean tryLock(String key, long leaseSeconds, long waitSeconds) {
        try {
            RLock lock = redissonClient.getLock(key);
            return lock.tryLock(waitSeconds, leaseSeconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 解锁
     *
     * @param key
     */
    public static void unlock(String key) {
        RLock lock = redissonClient.getLock(key);
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

    /**
     * set添加值
     *
     * @param key
     * @param vaule
     * @return
     */
    public static long setAdd(String key, String... vaule) {
        return redis.opsForSet().add(key, vaule);
    }

    /**
     * 获取set
     *
     * @param key
     * @return
     */
    public static Set<String> getSet(String key) {
        return redis.opsForSet().members(key);
    }

    /**
     * 删除set值
     *
     * @param key
     * @param values
     * @return
     */
    public static Long removeSet(String key, Object... values) {
        return redis.opsForSet().remove(key, values);
    }

    /**
     * 移除并返回集合中的一个随机元素。
     *
     * @param key
     * @return
     */
    public static String setPop(String key) {
        return redis.opsForSet().pop(key);
    }

    /**
     * 向redis的list集合加入元素
     *
     * @param key
     * @param value
     */
    public static void lpush(String key, String value) {
        redis.opsForList().leftPush(key, value);
    }

    public static void lpush(String key, String value, long time) {
        redis.opsForList().leftPush(key, value);
        if (time > 0) {
            expire(key, time);
        }
    }

    public static void lpush(String key, Collection<String> value) {
        redis.opsForList().rightPushAll(key, value);
    }

    public static boolean existsKey(String key) {
        return redis.hasKey(key);
    }

    public static List<String> lrange(String key, long start, long end) {
        return redis.opsForList().range(key, start, end);
    }

    public static void lrem(String key, long count, String value) {
        redis.opsForList().remove(key, count, value);
    }

    public static void deleteListAll(String key) {
        redis.opsForList().trim(key, 0, 0);
        redis.opsForList().leftPop(key);
        redis.delete(key);
    }

    /**
     * 匹配符合正则的所有key,此方式弥补keys的性能问题
     *
     * @param pattern
     * @return
     */
    public static Set<String> scan(String pattern) {
        return redis.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(pattern).count(1000).build())) {
                while (cursor.hasNext()) {
                    keysTmp.add(new String(cursor.next(), "Utf-8"));
                }
            } catch (Exception e) {
                log.error(e.getMessage());
                throw new RuntimeException(e);
            }
            return keysTmp;
        });
    }

    public static void delPattern(String pattern) {
        scan(pattern).forEach(key -> {
            del(key);
        });
    }
}
